package com.cd.security.filter;

import com.cd.common.constant.Constants;
import com.cd.common.constant.LqqConstant;
import com.cd.common.domain.LoginUser;
import com.cd.common.domain.ResponseResult;
import com.cd.common.exception.LqqException;
import com.cd.common.util.IpUtils;
import com.cd.common.util.JwtUtils;
import com.cd.common.util.RedisCache;
import com.cd.common.util.ServletUtils;
import com.cd.common.domain.SysUser;
import com.cd.system.service.UserService;
import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);


    @Autowired
    private RedisCache redisCache;

    @Autowired
    private UserService userService;

    @Value("${lqq.environment}")
    private String environment;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //用户id
        String userid;
        //获取访问等信息
        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        // 获取token
        String token = request.getHeader(Constants.AUTHORIZATION);

        //单点登录 采用添加cookie 然后302
//        String ssoToken = request.getParameter("SSOToken");
//        if (ssoToken!=null &&ssoToken.equals("customer")){
//            SysUser user=new SysUser();
//            user.setUserName("customer");
//            user.setPassword("goodJob321");
//            ResponseResult data = userService.login(user);
//            HashMap<String,String> map=(HashMap) data.getData();
//            //token=map.get("token");
//            token=Constants.TOKEN_PREFIX+map.get(Constants.TOKEN);
//            //设置请求头
//            response.addHeader("authorization",token);
//            //设置cookie
//            response.addCookie(new javax.servlet.http.Cookie(Constants.AUTHORIZATION,token));//重定向怎么设置端口 不行 前端做吧 记录ip 然后跳转
//            response.setContentType("application/json");
//            response.setCharacterEncoding("utf-8");
//            //获取一下本地ip
//            response.sendRedirect(IpUtils.getHostIp()+":10290/schedule/caiyun");
//            return ;
//            //尝试两种 第一种 302重定向 第二种正常走逻辑 第二种应该不可行
//            //实在不行就重定向一个页面 页面做跳转 参考jsp
//            //response.setStatus(302);
//
//        }

        //正常登录流程
        if (!StringUtils.hasText(token)) {
            // token为空或者没有数据 放行，让后面的过滤去干活
            filterChain.doFilter(request, response);
            return;
        }

        //对token进行校验
        if (token.startsWith(Constants.TOKEN_PREFIX)) {// 前端框架的cookie 解析

            token = token.split(Constants.TOKEN_PREFIX)[1];
            // 解析token
            try {
                Claims claims = JwtUtils.parseJWT(token);
                userid = claims.getSubject();
            } catch (Exception e) {
                logger.error("token非法:" + token);
                throw new LqqException(LqqConstant.TOKEN_ILLEGAL);
            }
            // 从redis中获取用户信息
            //有个小小的疑惑 如果这个userid是被模拟出来的 其他的值都是假的
            //这个问题确确实实的存在 因为key消失了 可以设置单个ip登录绑定ip或者mac验证
            //如1 lqq lqq 我模拟出来是的1 lq kl
            //这里应该加上混淆的ip
            String redisKey = LqqConstant.LOGINKEY_PREFIX + userid + "|" + IpUtils.getIpAddr();
            LoginUser loginUser = redisCache.getCacheObject(redisKey);
            if (Objects.isNull(loginUser)) {
                // 用户未登录，抛出异常
                throw new RuntimeException(LqqConstant.LOGIN_UNAUTHORIZED);
            }
            // 存入SecurityContextHolder
            // 获取权限信息封装到Authentication中
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(loginUser,
                            null,
                            loginUser.getAuthorities());//这个完全可以写null
            // 然后写入到上下文中
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            // 记录远程地址访问了本系统
            logger.info("{}({})({})访问了本系统的[{}]地址", loginUser.getUsername(), IpUtils.getIpAddr(), userAgent.getOperatingSystem().getName(), request.getContextPath() + request.getServletPath());
            //续时间 //后续判断时间 小于多少就续时间
            redisCache.expire(redisKey, 3600, TimeUnit.SECONDS);
            // 放行
            filterChain.doFilter(request, response);


            //如果是basic auth 请求 该方法目前没有记录 仅有日志
        } else if (token.startsWith(Constants.BASIC_AUTH_PREFIX)) {
            token = token.split(Constants.BASIC_AUTH_PREFIX)[1];
            String baseStr = new String(Base64.getDecoder().decode(token), StandardCharsets.UTF_8);
            String[] split = baseStr.split(":");
            String userName = split[0];
            String password = split[1];
            SysUser user = userService.findByUsername(userName);
            //如果密码正确则登录 错误则返回错误
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            if (passwordEncoder.matches(password, user.getPassword())) {
            } else {
                logger.error("用户名[{}]或密码错误，basic登录失败", userName);
                throw new RuntimeException(LqqConstant.LOGIN_UNAUTHORIZED);
            }
            LoginUser loginUser = new LoginUser();
            loginUser.setUser(user);
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(loginUser,
                            null,
                            null);//这个完全可以写null 有隐患 //后续修改为真实的
            // 然后写入到上下文中
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            // 记录远程地址访问了本系统
            logger.info("{}({})({})访问了本系统的[{}]地址", userName, IpUtils.getIpAddr(), userAgent.getOperatingSystem().getName(), request.getContextPath() + request.getServletPath());
            filterChain.doFilter(request, response);

            //后门程序
        } else if (token.equals("123456")) {
            if (!environment.equals("prod")) {
                logger.error("{}({})访问了本系统的[{}]地址", "非法用户", request.getRemoteAddr(), request.getContextPath() + request.getServletPath());
                LoginUser loginUser = new LoginUser();
                SysUser user = new SysUser();
                user.setUserName("admin");
                user.setPassword("123456");
                user.setUserId(1L);
                loginUser.setUser(user);
                loginUser.setPermissions(new HashSet<>());
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                filterChain.doFilter(request, response);
            } else {
                throw new LqqException(LqqConstant.TOKEN_ILLEGAL);
            }
        } else {
            logger.error(LqqConstant.TOKEN_ILLEGAL);
            throw new LqqException(LqqConstant.TOKEN_ILLEGAL);
        }

    }


}
